# -*- encoding: utf-8 -*-
import random
import re
import datetime
from . import generate_data as gd
from . import manual_data as md
from .validator import check_birthday_code


def get_no_args(id_no: str) -> dict:
    id_no = id_no.upper()
    if len(id_no) == 18:
        code = {
            'body': id_no[0:17],
            'address_code': id_no[0:6],
            'birthday_code': id_no[6:14],
            'order_code': id_no[14:17],
            'check_bit': id_no[17:18],
            'type': 18
        }
    else:
        code = {
            'body': id_no,
            'address_code': id_no[0:6],
            'birthday_code': '19' + id_no[6:12],
            'order_code': id_no[12:15],
            'check_bit': '',
            'type': 15
        }
    return code


def get_address_info(address_code: str, birthday_code: str, strict_mode=False) -> dict:
    province_code = address_code[0:2] + '0000'
    city_code = address_code[0:4] + '00'
    address_info = dict()
    address_info['province'] = get_address(province_code, birthday_code, strict_mode)
    address_info['city'] = ''
    address_info['district'] = ''
    # 如果不是港澳台身份证则获取市区县的信息
    if address_code[0] != '8':
        address_info['city'] = get_address(city_code, birthday_code, strict_mode)
        address_info['district'] = get_address(address_code, birthday_code, strict_mode)
    return address_info


# def get_address_from_code_change(address_code: str) -> tuple:
#     c_change = gd.code_change()
#     new_code = ''
#     new_name = ''
#     for code_change in c_change.values():
#         for code in code_change:
#             if code['old_code'] == address_code:
#                 new_code = code['new_code']
#                 new_name = code['new_name']
#             if new_code and new_name:
#                 break
#     return new_code, new_name
#
#
# def get_address_from_unaccounted(address_code: str) -> tuple:
#     unaccounted = gd.unaccounted()
#     new_code = ''
#     new_name = ''
#     for un_code in unaccounted.values():
#         for code in un_code:
#             if code.get('code') == address_code:
#                 new_code = code.get('code')
#                 new_name = code.get('name')
#             if new_code and new_name:
#                 break
#     return new_code, new_name


def get_address(address_code, birthday_code, strict_mode=False) -> str:
    ad_code_list = gd.all_district_code().get(address_code, [])
    if not ad_code_list:
        ad_code_list = md.additional_district_code().get(address_code, [])
    year = int(birthday_code[0:4])
    address = ''
    if ad_code_list:
        for val in ad_code_list:
            start_year = 0 if val['start_year'] == '' else val['start_year']
            end_year = 9999 if val['end_year'] == '' else val['end_year']
            if end_year >= year >= start_year:
                address = val['address']

        if not address and not strict_mode:
            for val in ad_code_list:
                address = val['address']
                break
    else:
        # 修复 \d\d\d\d01、\d\d\d\d02、\d\d\d\d11 和 \d\d\d\d20 的历史遗留问题
        # 以上四种地址码，现实身份证真实存在，但民政部历年公布的官方地址码中可能没有查询到
        # 如：440401 450111 等
        # 所以这里需要特殊处理
        # 1980年、1982年版本中，未有制定省辖市市辖区的代码，所有带县的省辖市给予“××××20”的“市区”代码。
        # 1984年版本开始对地级市（前称省辖市）市辖区制定代码，其中“××××01”表示市辖区的汇总码，同时撤销“××××20”的“市区”代码（追溯至1983年）。
        # 1984年版本的市辖区代码分为城区和郊区两类，城区由“××××02”开始排起，郊区由“××××11”开始排起，后来版本已不再采用此方式，已制定的代码继续沿用。
        suffixes = address_code[4:6]
        if suffixes == '01':
            address = '市辖区'
        if suffixes == '20':
            address = '市区'
        if suffixes == '02':
            address = '城区'
        if suffixes == '11':
            address = '郊区'
    return address


def get_address_by_code(code: str) -> list:
    all_code = gd.all_district_code()
    addcit_code = md.additional_district_code()
    name_change = gd.name_change()
    province_code = code[0:2].ljust(6, '0')
    city_code = code[0:4].ljust(6, '0')
    address_list = list()
    for c in [province_code, city_code, code]:
        code_list = all_code.get(c, [])
        if not code_list:
            code_list = addcit_code.get(c, [])
        name = ''
        if code_list:
            name = code_list[0]['address']
        for year, nc_item in name_change.items():
            nc_dict = {item['code']: item['new_name'] for item in nc_item}
            name = nc_dict.get(c, name)
        address_list.append(name)

    return address_list


def get_translation(address_code: str, birthday_code: str) -> tuple:
    year = int(birthday_code[0:4])
    now_year = datetime.datetime.now().year
    code_list = [str(address_code)]
    mergers = md.manual_merges()
    splits = md.manual_splits()
    code_change = gd.code_change()
    name_change = gd.name_change()
    unaccounted = gd.unaccounted()
    start_year = 1980 if year < 1981 else year

    for year in range(start_year - 1, now_year + 1):
        old_codes = code_list
        code_list = list()
        for oc in old_codes:
            if year in mergers and oc in mergers.get(year):
                code_list.append(mergers[year][oc])

            elif year in splits and oc in splits.get(year):
                code_list += splits[year][oc]

            elif year in code_change:
                cc_dict = {cc['old_code']: cc['new_code'] for cc in code_change[year]}
                new_code = cc_dict.get(oc, oc)
                code_list.append(new_code)

            elif year in name_change and oc in name_change[year]:
                code_list.append(name_change[year][oc])

            elif year in unaccounted and oc in unaccounted[year]:
                code_list.append(unaccounted[year][oc])

            else:
                code_list.append(oc)

    address_list = get_address_by_code(code_list[0])
    return '-'.join(address_list), address_list


def get_abandoned(address_code: str) -> int:
    """
    检测地址码是否废弃
    :param address_code:
    :return:
    """
    return 0 if gd.latest_address_code().get(address_code, 0) else 1


def get_constellation(birthday_code: str) -> str:
    """
    获取星座信息
    :param birthday_code:
    :return:
    """
    month = int(birthday_code[4:6])
    day = int(birthday_code[6:8])

    start_date = md.get_constellation()[month]['start_date']
    start_day = int(start_date.split('-')[-1])

    if day < start_day:
        tmp_month = 12 if month - 1 == 0 else month - 1
        return md.get_constellation()[tmp_month]['name']

    return md.get_constellation()[month]['name']


def get_chinese_zodiac(birthday_code: str) -> str:
    """
    获取生肖
    :param birthday_code:
    :return:
    """
    start = 1900  # 子鼠
    end = int(birthday_code[0:4])
    key = (end - start) % 12
    return md.get_chinese_zodiac()[key]


def get_random_address_code(pattern):
    """
    获取随机地址码
    :param pattern:
    :return:
    """
    pattern = re.compile(pattern)
    result = ['110000']
    for key in gd.latest_address_code().keys():
        if re.match(pattern, key):
            result.append(key)
    return result[random.choice(range(len(result)))]


def generator_check_bit(code_body: str) -> str:
    """
    生成校验码
    :param code_body:
    :return:
    """
    # 计算权重
    code_weight = dict()
    for i in range(2, 19)[::-1]:
        weight = pow(2, i - 1) % 11
        code_weight[i] = weight

    # 计算前十七位数与权重的乘积和
    body_sum = 0
    for index, val in enumerate(list(code_body)):
        body_sum += int(val) * code_weight[18 - index]

    check_bit = (12 - (body_sum % 11)) % 11
    if check_bit == 10:
        check_bit = 'X'
    return str(check_bit)


def generator_address_code(address: str) -> str:
    """
   生成地址码
   :param address:
   :return:
   """
    address_code = ''
    if not address:
        address_code, address = random.choice(list(gd.latest_address_code().items()))
    else:
        for key, val in gd.latest_address_code().items():
            if val == address:
                address_code = key
                break

    if address_code and address_code[0] == '8':
        return address_code

    if address_code:
        if address_code[2:6] == '0000':
            province_code = address_code[0:2]
            pattern = r'^%s\d{2}(?!00)[0-9]{2}$' % province_code
            address_code = get_random_address_code(pattern)

        if address_code[4:6] == '00':
            city_code = address_code[0:4]
            pattern = r'^%s(?!00)[0-9]{2}$' % city_code
            address_code = get_random_address_code(pattern)

    else:
        pattern = r'^\d{4}(?!00)[0-9]{2}$'
        address_code = get_random_address_code(pattern)

    return address_code


def generator_birthday_code(address_code: str, address: str, birthday: str = None) -> str:
    """
    生成出生日期码
    :param address_code:
    :param address:
    :param birthday:
    :return:
    """
    year = ''
    month = ''
    day = ''
    start_year = 0
    end_year = 9999

    if birthday:
        year = birthday[0:4]
        month = birthday[4:6] if birthday[4:6] else '1'
        day = birthday[6:8] if birthday[6:8] else '1'
        month = f'{int(month):02}'
        day = f'{int(day):02}'
    if len(year) < 4:
        year = '19' + str(random.randint(50, 99))

    code_list = gd.all_district_code().get(address_code, [])
    if not code_list:
        code_list = md.additional_district_code().get(address_code, [])

    if code_list:
        for val in code_list:
            if val['address'] == address:
                start_year = start_year if val['start_year'] == '' else val['start_year']
                end_year = end_year if val['end_year'] == '' else val['end_year']

    if year < str(start_year):
        year = str(start_year)

    if year > str(end_year):
        year = str(end_year)

    if not check_birthday_code(year + month + day):
        year = str(random.randint(max(1950, start_year), min(end_year, datetime.datetime.now().year) - 1))
        month = f'{random.randint(1, 12):02}'
        day = f'{random.randint(1, 28):02}'

    return year + month + day


def generator_order_code(sex=None) -> str:
    """
    生成顺序码
    :param sex:
    :return:
    """
    order_code = random.randint(101, 999)
    if sex and int(sex) != order_code % 2:
        order_code -= 1
    return str(order_code)
